﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Navigation;
using DeepForest.Phone.Assets.Tools;
using Lasalara.Model;
using Lasalara.ViewModel;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Net.NetworkInformation;
using Microsoft.Phone.Reactive;
using Microsoft.Phone.Shell;
using RestSharp;
using ServiceStack.Text;
using GestureEventArgs = System.Windows.Input.GestureEventArgs;

namespace Lasalara.View
{
    public partial class Books
    {
        #region fields
        private readonly BookViewModel _bookViewModel = new BookViewModel();

        internal static ApplicationBarIconButton AddButton;
        internal static ApplicationBarIconButton ResetButton;
        internal static ApplicationBarIconButton SyncButton;
        internal static ApplicationBarIconButton DeleteButton;

        internal static ApplicationBarMenuItem UpdateBookListItem;
        internal static ApplicationBarMenuItem ChangePasswordItem;
        internal static ApplicationBarMenuItem OpenTutorialItem;
        internal static ApplicationBarMenuItem LogOutItem;

        private bool _deleting; // Is the user supposed to select multiple books for delete
        private bool _updating; // Is the user supposed to select multiple books for update
        private bool _resetting; // Is the user supposed to select multiple books for reset.
        private int _currentBookToPing;
        private int _totalBooksToPing;
        private int _currentBookToUpdate;
        private int _totalBooksToUpdate;
        private int _totalChaptersToDownload;
        private int _currentChapterToDownload;
        private List<KeyValuePair<Chapter, string>> _updateQue = new List<KeyValuePair<Chapter, string>>();
        private List<Book> _downloadQue = new List<Book>();
        private bool _pingCancelled;
        private bool _updateCancelled;
        private bool _downloadCancelled;
        private static CancellationTokenSource _tokenSource;
        private CancellationToken _token;
        #endregion

        public Books()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Disables back button if user comes from login page, loads books
        /// </summary>
        protected override void OnNavigatedTo(NavigationEventArgs navigationEventArgs)
        {
            if (App.CurrentUser == null) { App.CurrentUser = GlobalViewModel.GetCurrentUser(); }
            InitializePage();
            UpdateTotalProgress();
            CreateAppBar();

            // If user is coming back from the study view.
            if (NavigationContext.QueryString.ContainsKey("fromStudyView"))
            {
                BookViewModel.RemovePivotPages(booksPivot,
                                               new List<PivotItem> { changePasswordPivotItem, addBooksPivotItem });
                PopulateAndSendToChapters(App.CurrentUser.CurrentBook);
            }
            else
            {
                //remove "change password", "add", and "chapters" page from booksPivot
                BookViewModel.RemovePivotPages(booksPivot,
                                               new List<PivotItem> { changePasswordPivotItem, chaptersPivotItem, addBooksPivotItem });
            }

            LoadBooksAsync(false);

            // Download tutorial the first time the user opens app.
            if (!App.CurrentUser.HasSeenTutorial)
            {
                DownloadTutorial();
                App.CurrentUser.HasSeenTutorial = true;
            }

            NavigationService.RemoveBackEntry(); // Remove back entry.
            if (NavigationContext.QueryString.ContainsKey("fromSponsor"))
            {
                if (NavigationContext.QueryString.ContainsKey("toAddBook"))
                {
                    AddButtonClick(new object(), new EventArgs());
                }
                if (NavigationContext.QueryString.ContainsKey("toChangePassword"))
                {
                    ChangePasswordClick(new object(), new EventArgs());
                }
            }
            NavigationContext.QueryString.Clear(); // Clear query string.
        }

        /// <summary>
        /// Sets the page logo and loads current user.
        /// </summary>
        private void InitializePage()
        {
            GlobalViewModel.SetLogo(booksLogo, booksLogoRectangle, "logo1");

            // Set lists itemsource to empty collection that can easily be updated
            booksListBox.ItemsSource = new ObservableCollection<Book>();
            chaptersListBox.ItemsSource = new ObservableCollection<Chapter>();
        }

        /// <summary>
        /// Saves settings when navigating away from page.
        /// </summary>
        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            var loggedIn = GlobalViewModel.GetAppSettings("loggedIn");
            if (loggedIn != null && !bool.Parse(loggedIn.ToString()))
            {
                GlobalViewModel.RemoveLiveTileProgress();
            }
            else
            {
                GlobalViewModel.UpdateLiveTileProgress(App.CurrentUser);
            }
        }

        /// <summary>
        /// Downloads the tutorial book for the app
        /// </summary>
        private void DownloadTutorial()
        {
            var email = AppResources.TutorialBookEmail;
            var title = AppResources.TutorialBookTitle;
            //check if tutorial book has been downloaded
            if (App.CurrentUser.TutorialBook != null)
            {
                PopulateAndSendToChapters(App.CurrentUser.TutorialBook);
            }
            else if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.AddingTutorial);
                _bookViewModel.AddBookWebRequest(email, title, App.CurrentUser, AddCallback);
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Error, AppResources.InternetProblem);
            }
        }

        /// <summary>
        /// Loads books from the phone storage and then from the server if that is requested
        /// </summary>
        private void LoadBooksAsync(bool fromServer)
        {
            var books = App.CurrentUser.DownloadedUserBooks.ToList();

            if (fromServer)
            {
                // First load books that are downloaded. Then insert new not downloaded books from server.
                books.ToObservable().
                    Zip(Observable.Interval(TimeSpan.FromMilliseconds(150)), (d, t) => d).
                    ObserveOnDispatcher().
                    Subscribe(AddBook, UpdateBooks);

            }
            else // don't update from server
            {
                books.AddRange(App.CurrentUser.NotDownloadedUserBooks.ToList()); // add list of not downloaded books to list, so that all books would be loaded
                books.ToObservable().
                    Zip(Observable.Interval(TimeSpan.FromMilliseconds(150)), (d, t) => d).
                    ObserveOnDispatcher().
                    Subscribe(AddBook);
            }
        }

        /// <summary>
        /// Loads chapters from the phone storage and downloads chapters that are not yet downloaded if online is true.
        /// </summary>
        private void LoadChaptersAsync(bool fromServer)
        {
            _downloadCancelled = false;
            chaptersListBox.ItemsSource = new ObservableCollection<Chapter>();
            _totalChaptersToDownload = App.CurrentUser.CurrentBook.Chapters.Count(x => !x.IsDownloaded);

            if (fromServer)
            {
                if (_totalChaptersToDownload > 0)
                {
                    _currentChapterToDownload = 1;
                    ShowChaptersDownloading(true);
                    LoadAndDownloadChapters(App.CurrentUser.CurrentBook);
                }
                else
                {
                    LoadChaptersAsync(false);
                }
            }
            else
            {
                App.CurrentUser.CurrentBook.Chapters.ToObservable().
                       Zip(Observable.Interval(TimeSpan.FromMilliseconds(150)), (d, t) => d).
                       ObserveOnDispatcher().
                       Subscribe(AddChapter, PostChaptersUpdate);
            }
        }

        /// <summary>
        /// Loops chapters and downloads chapters that are not downloaded.
        /// </summary>
        /// <param name="book">Book with the chapters that need to be loaded.</param>
        private async void LoadAndDownloadChapters(Book book)
        {
            foreach (var chapter in book.Chapters)
            {
                if (!chapter.IsDownloaded && !_downloadCancelled)
                {
                    _tokenSource = new CancellationTokenSource();
                    _token = _tokenSource.Token;
                    try
                    {
                        var result = await BookViewModel.GetQuestionsWebRequestAsync(chapter, _token);
                        GetQuestionsAutomaticCallback(result as RestResponse, chapter);
                    }
                    catch (Exception) // cancellationexception.
                    {
                        _downloadCancelled = true;
                        ShowChaptersDownloading(false);
                        AddChapter(chapter);
                    }
                }
                else
                {
                    AddChapter(chapter);
                }
            }
        }

        /// <summary>
        /// Shows or hides the appropriate controls when downloading chapters
        /// </summary>
        /// <param name="enabled">Enable or disable UI controls.</param>
        private void ShowChaptersDownloading(bool enabled)
        {
            ApplicationBar.IsVisible = !enabled;
            trayProgressBar.IsVisible = enabled;
            BookViewModel.EnableListBox(!enabled, chaptersListBox);
            GlobalViewModel.ShowCancelButton(enabled, cancelGridChapters);
        }

        /// <summary>
        /// Sync books and all contents with server
        /// </summary>
        /// <param name="books">Books to be synced with the server</param>
        private void SyncBooks(IEnumerable books)
        {
            var booksList = books.Cast<Book>().ToList();
            _totalBooksToPing = booksList.Count;
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.Updating);
                BookViewModel.EnableListBox(false, booksListBox);
                // Ping server and find what chapters have been changed and what have been added
                foreach (var book in booksList)
                {
                    if (!_pingCancelled)
                    {
                        GlobalViewModel.ShowCancelButton(true, cancelGridLibrary);
                        _bookViewModel.PingChaptersWebRequest(book, PingCallback);
                    }
                    else
                    {
                        PostBooksUpdate();
                        break;
                    }
                }
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Error, AppResources.CannotSyncWithNoInternet);
                SyncButton.IsEnabled = true;
            }
        }

        /// <summary>
        /// Adds a book to the list that's displayed in books view.
        /// </summary>
        /// <param name="book">Book to be added</param>
        private void AddBook(Book book)
        {
            var source = booksListBox.ItemsSource as ObservableCollection<Book>;
            if (!source.Contains(book)) { source.Add(book); }
        }
        /// <summary>
        /// Adds a chapter to the list that's displayed in chapters view.
        /// </summary>
        /// <param name="chapter">Chapter to be added</param>
        private void AddChapter(Chapter chapter)
        {
            var source = chaptersListBox.ItemsSource as ObservableCollection<Chapter>;
            if (!source.Contains(chapter) && App.CurrentUser.CurrentBook.Chapters.Contains(chapter)) { source.Add(chapter); }
        }

        /// <summary>
        /// Reset progress for the supplied books.
        /// </summary>
        /// <param name="booksList">Books to reset the progress for.</param>
        private void ResetBooks(IEnumerable booksList)
        {
            var books = booksList.Cast<Book>().ToList();
            foreach (var userBook in books.Select(book => App.CurrentUser.DownloadedUserBooks.First(x => x.Equals(book))))
            {
                userBook.Progress = 0;
                foreach (var chapter in userBook.Chapters.Where(x => x.IsDownloaded))
                {
                    chapter.Progress = 0;
                    chapter.CurrentQuestion = null;
                    foreach (var question in chapter.Questions)
                    {
                        question.AnswerCount = 0;
                        question.AnsweredDate = DateTime.Now.AddDays(-1);
                    }
                }
                UpdateBookAndChapterInBookList(userBook, null);
            }
            // If "currentBook" also has to be reset.
            if (books.Contains(App.CurrentUser.CurrentBook))
            {
                App.CurrentUser.CurrentBook = null;
                App.CurrentUser.CurrentChapter = null;
            }
        }

        /// <summary>
        /// Deletes the supplied books
        /// </summary>
        /// <param name="books">The books to be deleted</param>
        private void DeleteBooks(IEnumerable books)
        {
            foreach (Book book in books)
            {
                GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.DeletingInProgress);
                GlobalViewModel.ShowCancelButton(true, cancelGridLibrary);
                _bookViewModel.DeleteBookWebRequest(book.Bk, App.CurrentUser, DeleteCallback);
            }
        }

        /// <summary>
        /// Downloads the supplied book
        /// </summary>
        /// <param name="book">The book to be downloaded</param>
        private void DownloadBooks(Book book)
        {
            GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.DownloadInProgress);
            GlobalViewModel.ShowCancelButton(true, cancelGridLibrary);
            BookViewModel.EnableListBox(false, booksListBox);
            BookViewModel.EnableAppBarIcons(false);
            _bookViewModel.DownloadBookWebRequest(book, DownloadCallback);
        }

        /// <summary>
        /// Logs the user out and saves login data
        /// </summary>
        private void LogOut()
        {
            GlobalViewModel.SetAppSetting("loggedIn", false);
            NavigationService.Navigate(new Uri("/View/LoginRegister.xaml?loggedOut=true", UriKind.Relative));
        }

        /// <summary>
        /// Create chapters page if it does not exist, navigate to it and populate chapters page with book chapters
        /// and downloading questions for those chapters
        /// </summary>
        /// <param name="book">The book that contains the chapters</param>
        private void PopulateAndSendToChapters(Book book)
        {
            BookViewModel.CheckAndSelectPivotItem(booksPivot, chaptersPivotItem, myBooksPivotItem);
            UpdateChaptersProgressBar(book);
            App.CurrentUser.CurrentBook = book;
            LoadChaptersAsync(true);
        }

        /// <summary>
        /// Check if the checkbox needs to be checked or unchecked.
        /// </summary>
        /// <param name="button">The Application bar button corresponding to the current process</param>
        /// <param name="removedItems">The list of items that the user has deselected</param>
        /// <param name="shouldDelete">Whether a book should be able to be selected if it is not downloaded.</param>
        private void CheckIfSelectOrDeselect(ApplicationBarIconButton button, IList removedItems, bool shouldDelete)
        {
            var removed = false;
            if (removedItems.Count > 0)
            {
                var lastRemoved = removedItems[0] as Book;
                var lastRemovedListBoxItem = booksListBox.ItemContainerGenerator.ContainerFromItem(lastRemoved);
                if (lastRemovedListBoxItem != null)
                {
                    BookViewModel.ToggleCheckBox(lastRemovedListBoxItem);
                }
                removed = true;
            }
            if (booksListBox.SelectedItems.Count > 0 && !removed)
            {
                var lastAdded = booksListBox.SelectedItems[booksListBox.SelectedItems.Count - 1] as Book;
                if (!App.CurrentUser.DownloadedUserBooks.Contains(lastAdded) && !shouldDelete)
                {
                    booksListBox.SelectionChanged -= BooksListBoxSelectionChanged;
                    booksListBox.SelectedItems.Remove(lastAdded);
                    booksListBox.SelectionChanged += BooksListBoxSelectionChanged;
                    GlobalViewModel.ShowToast(AppResources.Notification, AppResources.CannotResetNotDownloaded);
                    return;
                }
                var lastSelectedListBoxItem = booksListBox.ItemContainerGenerator.ContainerFromItem(lastAdded);
                if (lastSelectedListBoxItem != null)
                {
                    BookViewModel.ToggleCheckBox(lastSelectedListBoxItem);
                }
                button.IsEnabled = true;
                if (button == DeleteButton) { BookViewModel.EnableDelete(true); }
                if (button == SyncButton) { BookViewModel.EnableSync(true); }
                if (button == ResetButton) { BookViewModel.EnableReset(true); }
            }
            else
            {
                if (booksListBox.SelectedItems.Count == 0)
                {
                    button.IsEnabled = false;
                    if (button == DeleteButton) { BookViewModel.EnableDelete(false); }
                    if (button == SyncButton) { BookViewModel.EnableSync(false); }
                    if (button == ResetButton) { BookViewModel.EnableReset(false); }
                }
            }
        }

        #region Update methods
        /// <summary>
        /// Updates the book and chapter in the bookslistbox and chapterslistbox so that new percentage would be displayed.
        /// </summary>
        private void UpdateBookAndChapterInBookList(Book book, Chapter chapter)
        {
            var source = booksListBox.ItemsSource as ObservableCollection<Book>;
            source[source.IndexOf(book)] = book;

            if (chapter != null)
            {
                var chapterSource = chaptersListBox.ItemsSource as ObservableCollection<Chapter>;
                chapterSource[chapterSource.IndexOf(chapter)] = chapter;
            }
        }

        /// <summary>
        /// Updates existing chapters and downloads new chapters if needed.
        /// </summary>
        private void UpdateAndAddNewChapters()
        {
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                if (_updateQue.Any())
                {
                    _totalBooksToUpdate = _updateQue.Count;
                    foreach (var update in _updateQue.TakeWhile(update => !_updateCancelled))
                    {
                        UpdateChapterAsync(update.Key, update.Value);
                    }
                }
                if (_downloadQue.Any())
                {
                    foreach (var download in _downloadQue.TakeWhile(download => !_downloadCancelled))
                    {
                        _bookViewModel.DownloadBookWebRequest(download, UpdateDownloadedBookWithNewChaptersCallback);
                    }
                }
                GlobalViewModel.ShowToast(AppResources.Success, AppResources.SelectedBooksUpdated);
                if (!_updateQue.Any() && !_downloadQue.Any())
                {
                    PostBooksUpdate();
                }
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Error, AppResources.InternetProblem);
            }
            _updateQue.Clear();
            _downloadQue.Clear();
        }

        /// <summary>
        /// Fetches new book list for user from server.
        /// </summary>
        private void UpdateBooks()
        {
            var passwordData = App.CurrentUser.Password;
            var passwordByte = ProtectedData.Unprotect(passwordData, null);
            var password = Encoding.UTF8.GetString(passwordByte, 0, passwordByte.Length);

            GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.Updating);
            GlobalViewModel.ShowCancelButton(true, cancelGridLibrary);
            _bookViewModel.UpdateBooksWebRequest(App.CurrentUser.Email, password, UpdateBooksAsyncCallback);
        }
        /// <summary>
        /// Update the supplied chapter by fetching fresh data from the server.
        /// </summary>
        /// <param name="chapter">The chapter that needs to be updated</param>
        /// <param name="version">The new chapter version</param>
        private void UpdateChapterAsync(Chapter chapter, string version)
        {
            GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.Updating);
            GlobalViewModel.ShowCancelButton(true, cancelGridLibrary);
            _bookViewModel.GetQuestionsWebRequest(chapter, version, ChapterUpdateCallback);

        }
        #endregion

        #region Post update methods
        /// <summary>
        /// Hides loading progress and shows appropriate buttons after updating book
        /// </summary>
        private void PostBooksUpdate()
        {
            booksListBox.SelectedIndex = -1;
            booksListBox.SelectionMode = SelectionMode.Single;
            if (App.CurrentUser.CurrentBook != null)
            {
                UpdateChaptersProgressBar(App.CurrentUser.CurrentBook);
            }
            GlobalViewModel.HideProgressBar(trayProgressBar);

            _totalBooksToPing = 0;
            _currentBookToPing = 0;
            _totalBooksToUpdate = 0;
            _currentBookToUpdate = 0;

            BookViewModel.EnableListBox(true, booksListBox);
            BookViewModel.ShowCheckBoxes(booksListBox, false, false);
            BookViewModel.EnableAppBarIcons(true);
            BookViewModel.EnableSync(false);
            BookViewModel.EnableDelete(false);
            BookViewModel.EnableReset(false);

            _deleting = false;
            _updating = false;
            _resetting = false;
            _pingCancelled = false;
            _updateCancelled = false;
            _downloadCancelled = false;

            UpdateTotalProgress();

        }
        #endregion

        #region validation

        /// <summary>
        /// Validates update data
        /// </summary>
        /// <param name="data">The Dictionary containing the values </param>
        private bool ValidateUpdate(IDictionary<string, string> data)
        {
            if (data != null)
            {
                if (data.ContainsKey("err"))
                {
                    if (data["err"] == "0") // Invalid username or password, ask user if they want to log in again.
                    {
                        NotificationTool.Show(AppResources.Error, AppResources.LogginAgain,
                                              new NotificationAction("OK", "OKActionTemplateKey", LogOut),
                                              new NotificationAction(AppResources.No, "CancelActionTemplateKey", () => { }));
                        return false;
                    }
                    NotificationTool.Show(AppResources.Error, AppResources.ErrorUpdatingPleaseLogIn,
                                          new NotificationAction("OK", "OKActionTemplateKey", LogOut),
                                          new NotificationAction(AppResources.No, "CancelActionTemplateKey", () => { }));
                    return false;
                }
                return true; // User was found
            }
            return false;
        }

        /// <summary>
        /// Validates add book fields
        /// </summary>
        private bool AreAddFieldsValid()
        {
            if (!String.IsNullOrEmpty(emailAddBookTextBox.Text) && !String.IsNullOrEmpty(titleAddBookTextBox.Text))
            {
                if (GlobalViewModel.ValidateEmail(emailAddBookTextBox.Text))
                {
                    return true;
                }
                GlobalViewModel.ShowToast(AppResources.Error, AppResources.WrongEmail);
                return false;
            }
            GlobalViewModel.ShowToast(AppResources.Error, AppResources.EnterAuthorsEmailAndPassword);
            return false;
        }

        #endregion

        #region progress updates

        /// <summary>
        /// Update the progressbar and all the chapter progresses in a background thread.
        /// </summary>
        private void UpdateTotalProgress()
        {
            var bw = new BackgroundWorker();
            bw.DoWork += (sender, args) =>
                             {
                                 foreach (var book in App.CurrentUser.DownloadedUserBooks)
                                 {
                                     foreach (var chapter in book.Chapters.Where(chapter => chapter.IsDownloaded))
                                     {
                                         foreach (var question in chapter.Questions.Where(question => question.AnsweredDate < DateTime.Now && question.AnswerCount > 0))
                                         {
                                             chapter.Progress = Chapter.GetChapterProgress(chapter);
                                         }
                                         BookViewModel.UpdateBookProgress(book);
                                     }
                                 }

                                 double totalQuestionsDownloaded =
                                     App.CurrentUser.DownloadedUserBooks.SelectMany(
                                         book => book.Chapters.Where(x => x.IsDownloaded)).Sum(
                                             chapter => chapter.Questions.Count);
                                 double totalQuestionsAnswered =
                                     App.CurrentUser.DownloadedUserBooks.SelectMany(
                                         book => book.Chapters.Where(x => x.IsDownloaded)).Sum(
                                             chapter => chapter.Questions.Count(x => x.AnsweredDate > DateTime.Now));
                                 var val = 0.0;
                                 if (!totalQuestionsAnswered.Equals(0))
                                 {
                                     val = totalQuestionsAnswered / totalQuestionsDownloaded * 100;
                                 }
                                 if (val > 100)
                                 {
                                     val = 100;
                                 }
                                 Dispatcher.BeginInvoke(() =>
                                                            {
                                                                totalProgressBar.Value = val;
                                                            });

                             };
            bw.RunWorkerAsync();
        }

        /// <summary>
        /// Update the progressbar for the book supplied
        /// </summary>
        /// <param name="book">The book to display progress for</param>
        private void UpdateChaptersProgressBar(Book book)
        {
            totalChaptersProgressBar.Value = book.Progress;
        }

        /// <summary>
        /// Hides progressbar and updates progressbar of currentbook if it is set.
        /// </summary>
        private void PostChaptersUpdate()
        {
            if (App.CurrentUser.CurrentBook != null)
            {
                UpdateChaptersProgressBar(App.CurrentUser.CurrentBook);
            }
            ApplicationBar.IsVisible = true;
            BookViewModel.EnableListBox(true, chaptersListBox);
            GlobalViewModel.ShowCancelButton(false, cancelGridChapters);
            _totalChaptersToDownload = 0;
            _currentChapterToDownload = 0;
            GlobalViewModel.HideProgressBar(trayProgressBar);
        }
        #endregion

        #region App bar

        /// <summary>
        /// Creates new application bar with buttons and event handlers
        /// </summary>
        private void CreateAppBar()
        {
            ApplicationBar = new ApplicationBar { Opacity = 0.4 };

            UpdateBookListItem = new ApplicationBarMenuItem(AppResources.SyncBookList);
            ChangePasswordItem = new ApplicationBarMenuItem(AppResources.ChangePasswordText);
            OpenTutorialItem = new ApplicationBarMenuItem(AppResources.OpenTutorialText);
            LogOutItem = new ApplicationBarMenuItem(AppResources.LogOutText);
            AddButton = new ApplicationBarIconButton
                             {
                                 IconUri = new Uri("/Images/appbar.new.rest.png", UriKind.Relative),
                                 IsEnabled = true,
                                 Text = AppResources.AddBookButtonText
                             };

            SyncButton = new ApplicationBarIconButton
                            {
                                IconUri = new Uri("/Images/appbar.sync.rest.png", UriKind.Relative),
                                IsEnabled = true,
                                Text = AppResources.SyncText
                            };
            DeleteButton = new ApplicationBarIconButton
                            {
                                IconUri = new Uri("/Images/appbar.delete.rest.png", UriKind.Relative),
                                IsEnabled = true,
                                Text = AppResources.DeleteButtonText
                            };

            ResetButton = new ApplicationBarIconButton
                            {
                                IconUri = new Uri("/Images/appbar.reset.png", UriKind.Relative),
                                IsEnabled = true,
                                Text = AppResources.Reset
                            };
            ApplicationBar.StateChanged += AppBarStateChanged;

            AddButton.Click += AddButtonClick;
            SyncButton.Click += SyncButtonClick;
            DeleteButton.Click += DeleteButtonClick;
            ResetButton.Click += ResetProgressClick;

            UpdateBookListItem.Click += UpdateBookListItemClick;
            ChangePasswordItem.Click += ChangePasswordClick;
            OpenTutorialItem.Click += (sender, args) => DownloadTutorial();
            LogOutItem.Click += LogOutClick;

            ApplicationBar.Buttons.Add(AddButton);
            ApplicationBar.Buttons.Add(SyncButton);
            ApplicationBar.Buttons.Add(DeleteButton);
            ApplicationBar.Buttons.Add(ResetButton);

            ApplicationBar.MenuItems.Add(UpdateBookListItem);
            ApplicationBar.MenuItems.Add(ChangePasswordItem);
            ApplicationBar.MenuItems.Add(OpenTutorialItem);
            ApplicationBar.MenuItems.Add(LogOutItem);
        }

        #endregion

        #region callbacks

        /// <summary>
        /// Callback method for automatically downloaded chapter questions
        /// </summary>
        private void GetQuestionsAutomaticCallback(RestResponse restResponse, Chapter chapter)
        {
            _bookViewModel.RequestHandle = null;
            if (restResponse.ResponseStatus == ResponseStatus.Completed)
            {
                var result = JsonSerializer.DeserializeFromString<Dictionary<string, string>>(restResponse.Content);
                if (result.ContainsKey("questions") && result.ContainsKey("answers"))
                {
                    var questionList = JsonSerializer.DeserializeFromString<List<string>>(result["questions"]);
                    var answerList = JsonSerializer.DeserializeFromString<List<string>>(result["answers"]);
                    var resultList = questionList.Select((t, i) => new Question
                    {
                        AnswerText = answerList[i],
                        QuestionText = t
                    }).ToList();
                    chapter.IsDownloaded = true;
                    chapter.Questions = resultList;
                }
                AddChapter(chapter);
            }
            if (_totalChaptersToDownload != 0 && _currentChapterToDownload != 0)
            {
                trayProgressBar.IsVisible = true;
                trayProgressBar.Value = (double)_currentChapterToDownload / _totalChaptersToDownload;
                trayProgressBar.Text = AppResources.DownloadInProgress + ": " + _currentChapterToDownload + " / " + _totalChaptersToDownload;

                if (_totalChaptersToDownload == _currentChapterToDownload)
                {
                    PostChaptersUpdate();
                }
            }
            _currentChapterToDownload++;
        }

        /// <summary>
        /// Callback method for chapter update
        /// </summary>
        private void ChapterUpdateCallback(RestResponse restResponse, Chapter chapter, string version)
        {
            GlobalViewModel.ShowCancelButton(false, cancelGridLibrary);
            _currentBookToUpdate++;
            _bookViewModel.RequestHandle = null;
            if (restResponse.ResponseStatus == ResponseStatus.Completed)
            {
                var result = JsonSerializer.DeserializeFromString<Dictionary<string, string>>(restResponse.Content);
                var answerList = JsonSerializer.DeserializeFromString<List<string>>(result["answers"]);
                var questionList = JsonSerializer.DeserializeFromString<List<string>>(result["questions"]);

                // Create list of questions from result.
                var callbackQuestions = questionList.Select((t, y) => new Question()
                {
                    AnswerCount = 0,
                    AnsweredDate =
                    DateTime.Now.AddDays(-1),
                    AnswerText = answerList[y],
                    QuestionText = t
                }).ToList();

                // Add questions that currenty do not exist on the phone.
                foreach (var callbackQuestion in callbackQuestions.Where(callbackQuestion => !chapter.Questions.Contains(callbackQuestion)))
                {
                    chapter.Questions.Add(callbackQuestion);
                }

                // Remove questions from phone that have been deleted from the server.
                for (var q = 0; q < chapter.Questions.Count; q++)
                {
                    var shouldRemove = callbackQuestions.All(callbackQuestion => !chapter.Questions[q].Equals(callbackQuestion));
                    if (shouldRemove)
                    {
                        chapter.Questions.RemoveAt(q);
                    }
                }

                chapter.Version = version;
                chapter.IsDownloaded = true;
                chapter.Progress = Chapter.GetChapterProgress(chapter);

                // Insert the updated chapter to the book it belongs to
                var updatedBook = BookViewModel.InsertChapterToUserBooks(App.CurrentUser, chapter);

                BookViewModel.UpdateBookProgress(updatedBook); // Update that books progress based on chapter progresses.
                UpdateChaptersProgressBar(updatedBook); // Update the progressbar on the chapters page based on the progress of the book.
                UpdateBookAndChapterInBookList(updatedBook, chapter);
            }
            else
            {
                // If user has cancelled request don't show error
                if (restResponse.ResponseStatus != ResponseStatus.TimedOut && restResponse.ResponseStatus != ResponseStatus.Aborted)
                {
                    _updateCancelled = true;
                    PostBooksUpdate();
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorUpdatingChapter);
                }
            }
            if (_totalBooksToUpdate == _currentBookToUpdate)
            {
                PostBooksUpdate();
            }
        }
        /// <summary>
        /// Callback method for chapter ping. Decides if chapter needs to be updated or added.
        /// </summary>
        private void PingCallback(RestResponse restResponse, Book book)
        {
            GlobalViewModel.ShowCancelButton(false, cancelGridLibrary);
            _bookViewModel.RequestHandle = null;
            _currentBookToPing++;
            var newChapters = false;
            if (restResponse.ResponseStatus == ResponseStatus.Completed && !String.IsNullOrEmpty(restResponse.Content))
            {
                var results = JsonSerializer.DeserializeFromString<List<string>>(restResponse.Content);

                //check if results actually contains chapters
                var firstChapterString = JsonSerializer.DeserializeFromString<Dictionary<string, string>>(results.First());
                var stringValue = firstChapterString.First().Value;
                //chapter was found on server
                if (stringValue != null)
                {
                    foreach (var result in results)
                    {
                        var chapterString = JsonSerializer.DeserializeFromString<Dictionary<string, string>>(result);
                        var chapterKey = chapterString["ck"];
                        var version = chapterString["version"];

                        var chapter = book.Chapters.FirstOrDefault(x => x.ChapterKey.Equals(chapterKey));

                        // If a chapter with the downloaded key is not found and the chapter needs to be added.
                        if (chapter == null && !newChapters)
                        {
                            newChapters = true;
                            _downloadQue.Add(book);
                            continue;
                        }
                        // If a chapter with the downloaded key is found, but has a different version, Add the chapter to update list
                        if (chapter != null && chapter.IsDownloaded)
                        {
                            if (!chapter.Version.Equals(version))
                            {
                                _updateQue.Add(new KeyValuePair<Chapter, string>(chapter, version));
                            }
                        }
                    }
                }
                //book is not found on server
                else
                {
                    PostBooksUpdate();
                    GlobalViewModel.ShowToast(AppResources.Error, book.Title +": " + AppResources.ErrorBookNotFoundOnServer);
                    return;
                }
            }
            else
            {
                // If user has cancelled request don't show error and empty update and download lists
                if (restResponse.ResponseStatus != ResponseStatus.TimedOut && restResponse.ResponseStatus != ResponseStatus.Aborted)
                {
                    _downloadQue.Clear();
                    _updateQue.Clear();
                    _pingCancelled = true;
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorUpdatingBooks);
                }
            }

            // Make update and download web request after all pings have been made.
            if (_currentBookToPing == _totalBooksToPing)
            {
                UpdateAndAddNewChapters();
            }
        }

        /// <summary>
        /// Callback method for downloaded book updatings
        /// </summary>
        private void UpdateDownloadedBookWithNewChaptersCallback(RestResponse restResponse, Book book)
        {
            GlobalViewModel.ShowCancelButton(false, cancelGridLibrary);
            _bookViewModel.RequestHandle = null;
            if (restResponse.ResponseStatus == ResponseStatus.Completed)
            {
                var listStr = JsonSerializer.DeserializeFromString<List<string>>(restResponse.Content);
                // Get only chapters that are not already downloaded
                var newChapters = (from item in listStr
                                   select JsonSerializer.DeserializeFromString<Dictionary<string, string>>(item)
                                       into bkString
                                       where !App.CurrentUser.DownloadedUserBooks.Any(x => x.Chapters.Any(z => z.ChapterKey.Equals(bkString["ck"])))
                                       select new Chapter
                                                   {
                                                       ChapterTitle = bkString["title"],
                                                       ChapterKey = bkString["ck"],
                                                       AuthorEmail = bkString["email"],
                                                       Version = bkString["version"],
                                                       AuthorName = bkString["name"],
                                                       AllowProposals = Convert.ToBoolean(bkString["allowProp"]),
                                                       Doctorate = Convert.ToBoolean(bkString["dr"])
                                                   }).ToList();


                if (!newChapters.IsNullOrEmpty())
                {
                    // Add new chapters to book
                    book.Chapters.AddRange(newChapters);
                    BookViewModel.UpdateBookProgress(book);

                    // Find the book from the listbox and replace with updated book
                    UpdateBookAndChapterInBookList(book, null);
                }
                else
                {
                    BookViewModel.EnableListBox(true, booksListBox);
                    return;
                }
            }
            else
            {
                // If user has cancelled request don't show error
                if (restResponse.ResponseStatus != ResponseStatus.TimedOut && restResponse.ResponseStatus != ResponseStatus.Aborted)
                {
                    _downloadCancelled = true;
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorUpdatingBooks);
                }
            }
            PostBooksUpdate();
        }

        /// <summary>
        /// Callback method for book updating
        /// </summary>
        private void UpdateBooksAsyncCallback(RestResponse restResponse)
        {
            BookViewModel.EnableListBox(true, booksListBox);
            GlobalViewModel.ShowCancelButton(false, cancelGridLibrary);
            _bookViewModel.RequestHandle = null;
            if (restResponse.ResponseStatus == ResponseStatus.Completed && !String.IsNullOrEmpty(restResponse.Content))
            {
                var response = JsonSerializer.DeserializeFromString<Dictionary<string, string>>(restResponse.Content);

                // Check if there are any errors with the user account.
                if (ValidateUpdate(response))
                {
                    var books = BookViewModel.GetBooksFromDictionary(response).ToList();
                    // If books from web request already exists remove them from the list
                    var newBooks = books.Where(book => !App.CurrentUser.DownloadedUserBooks.Contains(book) && !App.CurrentUser.NotDownloadedUserBooks.Contains(book)).ToList();

                    App.CurrentUser.NotDownloadedUserBooks.AddRange(newBooks); // Set not downloaded books to user

                    newBooks.ToObservable().
                        Zip(Observable.Interval(TimeSpan.FromMilliseconds(50)), (d, t) => d).
                        ObserveOnDispatcher().
                        Subscribe(AddBook, PostBooksUpdate);
                }
                else
                {
                    PostBooksUpdate();
                }
            }
            else
            {
                // If user has cancelled request don't show error
                if (restResponse.ResponseStatus != ResponseStatus.TimedOut && restResponse.ResponseStatus != ResponseStatus.Aborted)
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorUpdatingBooks);
                }
                PostBooksUpdate();
            }
        }

        /// <summary>
        /// Callback method for downloading chapter questions
        /// </summary>
        private void GetQuestionsManualCallback(RestResponse restResponse, Chapter chapter, string version)
        {
            GlobalViewModel.ShowCancelButton(false, cancelGridChapters);
            BookViewModel.EnableListBox(true, chaptersListBox);
            _bookViewModel.RequestHandle = null;
            if (restResponse.ResponseStatus == ResponseStatus.Completed)
            {
                var result = JsonSerializer.DeserializeFromString<Dictionary<string, string>>(restResponse.Content);
                if (result.ContainsKey("questions") && result.ContainsKey("answers"))
                {
                    var questionList = JsonSerializer.DeserializeFromString<List<string>>(result["questions"]);
                    var answerList = JsonSerializer.DeserializeFromString<List<string>>(result["answers"]);
                    var resultList = questionList.Select((t, i) => new Question
                                                                       {
                                                                           AnswerText = answerList[i],
                                                                           QuestionText = t
                                                                       }).ToList();
                    chapter.IsDownloaded = true;
                    chapter.Questions = resultList;

                    App.CurrentUser.CurrentChapter = chapter;
                    App.CurrentUser.CurrentChapter.CurrentQuestion =
                        chapter.Questions.FirstOrDefault(x => x.AnsweredDate < DateTime.Now);

                    if (resultList.Any())
                    {
                        NavigationService.Navigate(new Uri("/View/Study.xaml", UriKind.Relative));
                    }
                    else
                    {
                        // If selected chapter has no questions
                        GlobalViewModel.ShowToast(AppResources.Error, AppResources.NoQuestions);
                    }
                }
                else
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorDownloadingChapters);
                }
            }
            else
            {
                // If user has cancelled request don't show error
                if (restResponse.ResponseStatus != ResponseStatus.TimedOut && restResponse.ResponseStatus != ResponseStatus.Aborted)
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorDownloadingChapters);
                }
            }
            chaptersListBox.SelectedIndex = -1;
            GlobalViewModel.HideProgressBar(trayProgressBar);
            UpdateChaptersProgressBar(App.CurrentUser.CurrentBook);
        }

        /// <summary>
        /// Callback method for adding book
        /// </summary>
        private void AddCallback(RestResponse restResponse)
        {
            GlobalViewModel.ShowCancelButton(false, cancelGridAddBooks);
            _bookViewModel.RequestHandle = null;
            if (restResponse.ResponseStatus == ResponseStatus.Completed && !String.IsNullOrEmpty(restResponse.Content))
            {
                var response = JsonSerializer.DeserializeFromString<Dictionary<string, string>>(restResponse.Content);
                if (response.ContainsKey("err"))
                {
                    switch (response["err"])
                    {
                        case "0":
                            GlobalViewModel.ShowToast(AppResources.Error, AppResources.AddBookEmailNotFound);
                            break;
                        case "1":
                            GlobalViewModel.ShowToast(AppResources.Error, AppResources.AddTitleNotFound);
                            break;
                        case "2":
                            GlobalViewModel.ShowToast(AppResources.Notification, AppResources.AddBookAlreadyFound);
                            break;
                        default:
                            GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorAddingBook);
                            break;
                    }
                    GlobalViewModel.HideProgressBar(trayProgressBar);
                }
                else
                {
                    if (response.ContainsKey("bk"))
                    {
                        var book = BookViewModel.GetBookFromDictionary(response);
                        GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.DownloadInProgress);
                        _bookViewModel.DownloadBookWebRequest(book, DownloadCallback);
                    }
                    else
                    {
                        GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorAddingBook);
                        GlobalViewModel.HideProgressBar(trayProgressBar);
                    }
                }
            }
            else
            {
                // If user has cancelled request don't show error
                if (restResponse.ResponseStatus != ResponseStatus.TimedOut && restResponse.ResponseStatus != ResponseStatus.Aborted)
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorAddingBook);
                }
                GlobalViewModel.HideProgressBar(trayProgressBar);
            }
        }

        /// <summary>
        /// Callback method for changing password
        /// </summary>
        private void ChangePasswordCallback(RestResponse restResponse, string password)
        {
            _bookViewModel.RequestHandle = null;
            if (restResponse.ResponseStatus == ResponseStatus.Completed)
            {
                switch (restResponse.Content)
                {
                    case "":
                        App.CurrentUser.Password = GlobalViewModel.EncryptData(password);
                        GlobalViewModel.ShowToast(AppResources.Success, AppResources.PasswordChanged);
                        newPasswordTextBox.Password = "";
                        oldPasswordTextBox.Password = "";
                        booksPivot.SelectedItem = myBooksPivotItem;
                        PostBooksUpdate();
                        break;
                    case "0":
                        GlobalViewModel.ShowToast(AppResources.Error, AppResources.OldPasswordInValid);
                        GlobalViewModel.HideProgressBar(trayProgressBar);
                        break;
                    default:
                        GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorChangingPassword);
                        GlobalViewModel.HideProgressBar(trayProgressBar);
                        break;
                }
            }
            else
            {
                // If user has cancelled request don't show error
                if (restResponse.ResponseStatus != ResponseStatus.TimedOut && restResponse.ResponseStatus != ResponseStatus.Aborted)
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorChangingPassword);
                }
                GlobalViewModel.HideProgressBar(trayProgressBar);
            }
        }

        /// <summary>
        /// Callback method for deleting book
        /// </summary>
        private void DeleteCallback(RestResponse restResponse, string bookKey)
        {
            GlobalViewModel.ShowCancelButton(false, cancelGridLibrary);
            _bookViewModel.RequestHandle = null;
            // Allow deleting book that has already been deleted from server and returns 500 server error.
            if (restResponse.ResponseStatus == ResponseStatus.Completed && (restResponse.Content == "" || restResponse.StatusCode == System.Net.HttpStatusCode.InternalServerError))
            {
                var source = booksListBox.ItemsSource as ObservableCollection<Book>;
                if (source != null)
                {
                    foreach (var book in source.Where(book => book.Bk == bookKey))
                    {
                        source.Remove(book);

                        App.CurrentUser.DownloadedUserBooks.Remove(book);

                        //if book hasn't been downloaded, remove it from list
                        if (!book.IsDownloaded)
                        {
                            App.CurrentUser.NotDownloadedUserBooks.Remove(book);
                        }
                        // Remove tutorial book separately
                        if (book.Equals(App.CurrentUser.TutorialBook))
                        {
                            App.CurrentUser.TutorialBook = null;
                        }
                        // Remove currently active book separately
                        if (book.Equals(App.CurrentUser.CurrentBook))
                        {
                            App.CurrentUser.CurrentBook = null;
                            App.CurrentUser.CurrentChapter = null;
                        }
                        break;
                    }
                }
            }
            else
            {
                // If user has cancelled request don't show error
                if (restResponse.ResponseStatus != ResponseStatus.TimedOut && restResponse.ResponseStatus != ResponseStatus.Aborted)
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorDeletingBooks);
                }
            }
            PostBooksUpdate();
        }

        /// <summary>
        /// Callback method for downloading book
        /// </summary>
        private void DownloadCallback(RestResponse restResponse, Book book)
        {
            GlobalViewModel.ShowCancelButton(false, cancelGridLibrary);
            _bookViewModel.RequestHandle = null;
            if (restResponse.ResponseStatus == ResponseStatus.Completed)
            {
                // Deserialize web request result into a list of chapters
                var listStr = JsonSerializer.DeserializeFromString<List<string>>(restResponse.Content);
                // If result is an empty string that book does not exist anymore.
                if (string.IsNullOrEmpty(restResponse.Content))
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.BookDeletedBecauseRemovedFromServer);
                    DeleteBooks(new[] { book });
                    LoadBooksAsync(false);
                    return;
                }
                var chapters =
                    listStr.Select(JsonSerializer.DeserializeFromString<Dictionary<string, string>>).
                        Select(chapterDictionary => new Chapter
                                                        {
                                                            ChapterTitle = chapterDictionary["title"],
                                                            ChapterKey = chapterDictionary["ck"],
                                                            AuthorEmail = chapterDictionary["email"],
                                                            Version = chapterDictionary["version"],
                                                            AuthorName = chapterDictionary["name"],
                                                            AllowProposals =
                                                                Convert.ToBoolean(chapterDictionary["allowProp"]),
                                                            Doctorate = Convert.ToBoolean(chapterDictionary["dr"]),
                                                            Progress = 0
                                                        }).ToList();

                if (!chapters.IsNullOrEmpty())
                {
                    book.Chapters = chapters;
                    book.IsDownloaded = true;

                    var isTutorial = false;
                    if (book.Title.Equals(AppResources.TutorialBookTitle, StringComparison.OrdinalIgnoreCase) && book.Email.Equals(AppResources.TutorialBookEmail, StringComparison.OrdinalIgnoreCase))
                    {
                        App.CurrentUser.TutorialBook = book; // if book is tutorial
                        isTutorial = true;
                    }

                    App.CurrentUser.DownloadedUserBooks.Add(book); // add book to downloaded
                    App.CurrentUser.NotDownloadedUserBooks.Remove(book); // remove from not downloaded
                    App.CurrentUser.CurrentBook = book;

                    // Send user to chapters page and populate it
                    if (!isTutorial)
                    {
                        PopulateAndSendToChapters(book);
                    }

                    // Remove not downloaded book and add as downloaded book in position nr1.
                    var source = booksListBox.ItemsSource as ObservableCollection<Book>;
                    source.Remove(book);
                    source.Insert(0, book);
                    booksListBox.ItemsSource = null;
                    booksListBox.ItemsSource = source;

                }
                else
                {
                    NotificationTool.Show(AppResources.Notification, AppResources.NoChapters,
                                          new NotificationAction(AppResources.Yes, "OKActionTemplateKey", () => DeleteBooks(new[] { book })),
                                          new NotificationAction(AppResources.No, "CancelActionTemplateKey", () => {}));
                    BookViewModel.CheckAndSelectPivotItem(booksPivot, myBooksPivotItem, myBooksPivotItem);
                }
            }
            else
            {
                // If user has cancelled request don't show error
                if (restResponse.ResponseStatus != ResponseStatus.TimedOut && restResponse.ResponseStatus != ResponseStatus.Aborted)
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.ErrorDownloadingBooks);
                }
            }
            PostBooksUpdate();
        }

        #endregion

        #region eventHandlers
        /// <summary>
        /// Handler for cancel event.
        /// </summary>
        private void CancelTap(object sender, GestureEventArgs gestureEventArgs)
        {
            GlobalViewModel.ShowCancelButton(false, cancelGridChapters);
            GlobalViewModel.ShowCancelButton(false, cancelGridLibrary);
            GlobalViewModel.ShowCancelButton(false, cancelGridAddBooks);
            GlobalViewModel.ShowCancelButton(false, cancelGridChangePass);
            _tokenSource.Cancel();
            if (_bookViewModel.RequestHandle != null)
            {
                _bookViewModel.RequestHandle.Abort();
            }
        }

        /// <summary>
        /// Application bar delete button handler. Let user select books to be deleted.
        /// </summary>
        private void DeleteButtonClick(object sender, EventArgs e)
        {
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                if (!_deleting)
                {
                    _deleting = true;
                    GlobalViewModel.ShowToast(AppResources.Notification, AppResources.SelectBooksToDelete);
                    booksListBox.SelectionMode = SelectionMode.Multiple;
                    BookViewModel.ShowCheckBoxes(booksListBox, true, false);
                    BookViewModel.EnableAppBarIcons(false);
                }

                else // If books have been selected and delete button is pressed, delete the books.
                {
                    var books = booksListBox.SelectedItems;
                    NotificationTool.Show(AppResources.Confirmation, AppResources.ConfirmPermanentBookDelete,
                                          new NotificationAction(AppResources.Yes, "OKActionTemplateKey",
                                                                 () => DeleteBooks(books)),
                                          new NotificationAction(AppResources.No, "CancelActionTemplateKey", () => { }));

                }
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Notification, AppResources.CannotDeleteWithNoInternet);
            }
        }

        /// <summary>
        /// Back button event handler, with doubletap exit
        /// </summary>
        protected override void OnBackKeyPress(CancelEventArgs e)
        {
            e.Cancel = true;
            //if user has not finished downloading chapters, cancel download
            if (_currentChapterToDownload != _totalChaptersToDownload)
            {
                CancelTap(cancelGridChapters,null);
            }
            // If user has chosen update mode or delete mode disable that mode
            if (_deleting || _updating || _resetting)
            {
                PostBooksUpdate();
            }
            else
            {
                if (booksPivot.SelectedItem != null && booksPivot.SelectedItem != myBooksPivotItem)
                {
                    booksPivot.SelectedItem = myBooksPivotItem;
                }
                else
                {
                    e.Cancel = false;
                }
            }
        }

        /// <summary>
        /// Books view is switching to another page. This decides what buttons should be enabled and disabled.
        /// </summary>
        private void BooksPivotSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var previousItem = e.RemovedItems[0] as PivotItem;
            var currentItem = e.AddedItems[0] as PivotItem;

            if (previousItem == null || !booksPivot.Items.Contains(previousItem)) return;
            // If "change password" page is visible remove it when navigating away from it.
            if (previousItem == changePasswordPivotItem)
            {
                oldPasswordTextBox.Password = "";
                newPasswordTextBox.Password = "";
                booksPivot.Items.Remove(changePasswordPivotItem);
                booksListBox.SelectedIndex = -1;
            }
            // If "chapters" page is visible remove it when navigating away from it and set booksListBox selection to nothing
            if (previousItem == chaptersPivotItem)
            {
                booksPivot.Items.Remove(chaptersPivotItem);
                booksListBox.SelectedIndex = -1;
            }
            // If "add books" page is visible remove it when navigating away from it.
            if (previousItem == addBooksPivotItem)
            {
                emailAddBookTextBox.Text = "";
                titleAddBookTextBox.Text = "";
                booksPivot.Items.Remove(addBooksPivotItem);
            }
            // If user navigates to main library page show the application bar
            if (currentItem == myBooksPivotItem)
            {
                ApplicationBar.Mode = ApplicationBarMode.Default;
                ApplicationBar.Opacity = 0.9;
                PostBooksUpdate();
                GlobalViewModel.SetLogo(booksLogo, booksLogoRectangle, "logo1");
            }
            // If user navigates to chapters page minimize the application bar and hide add button
            if (currentItem == chaptersPivotItem)
            {
                ApplicationBar.Mode = ApplicationBarMode.Minimized;
                ApplicationBar.Opacity = 0.4;
                AddButton.IsEnabled = true;
                SyncButton.IsEnabled = false;
                DeleteButton.IsEnabled = false;
                ResetButton.IsEnabled = false;
                GlobalViewModel.SetLogo(chaptersLogo, chaptersLogoRectangle, "logo2");
            }
            // If user navigates to change password page show the application bar and add button and disable sync button
            if (currentItem == changePasswordPivotItem)
            {
                ApplicationBar.Mode = ApplicationBarMode.Default;
                ApplicationBar.Opacity = 0.9;
                AddButton.IsEnabled = true;
                SyncButton.IsEnabled = false;
                DeleteButton.IsEnabled = false;
                ResetButton.IsEnabled = false;
                BookViewModel.ShowCheckBoxes(booksListBox, false, false);
            }
            // If user navigates to add book page show the application bar and hide all app bar buttons
            if (currentItem == addBooksPivotItem)
            {
                emailAddBookTextBox.Text = "";
                titleAddBookTextBox.Text = "";
                ApplicationBar.Mode = ApplicationBarMode.Default;
                ApplicationBar.Opacity = 0.4;
                BookViewModel.EnableAppBarIcons(false);
                BookViewModel.ShowCheckBoxes(booksListBox, false, false);
            }
            BookViewModel.EnableSync(false);
            BookViewModel.EnableDelete(false);
            BookViewModel.EnableReset(false);
            _updating = false;
            _deleting = false;
            _resetting = false;
        }

        /// <summary>
        /// Change password button click event handler. Validates password fields and initiates password change.
        /// </summary>
        private void ChangePasswordButtonClick(object sender, RoutedEventArgs e)
        {
            if (BookViewModel.ValidateChangePassword(oldPasswordTextBox, newPasswordTextBox))
            {
                var oldPass = oldPasswordTextBox.Password.ToLower();
                var newPass = newPasswordTextBox.Password.ToLower();
                GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.ChangePasswordInProgress);
                _bookViewModel.ChangePasswordWebRequest(App.CurrentUser, oldPass, newPass, ChangePasswordCallback);
            }
        }

        /// <summary>
        /// Makes the application bar transparent if the menu is not opened
        /// </summary>
        private void AppBarStateChanged(object sender, ApplicationBarStateChangedEventArgs e)
        {
            ApplicationBar.Opacity = e.IsMenuVisible == false ? 0.4 : 0.9;
        }

        /// <summary>
        /// Change password menu item click handler. Adds change password pivot page and navigates to it.
        /// </summary>
        private void ChangePasswordClick(object sender, EventArgs e)
        {
            BookViewModel.CheckAndSelectPivotItem(booksPivot, changePasswordPivotItem, myBooksPivotItem);
        }

        /// <summary>
        /// sync book list menu item click handler. Downloads a list of boks.
        /// </summary>
        private void UpdateBookListItemClick(object sender, EventArgs e)
        {
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                BookViewModel.EnableAppBarIcons(false);
                BookViewModel.EnableListBox(false, booksListBox);
                if (booksPivot.SelectedItem != myBooksPivotItem)
                {
                    BookViewModel.CheckAndSelectPivotItem(booksPivot, myBooksPivotItem, myBooksPivotItem);
                }
                LoadBooksAsync(true);
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Notification, AppResources.CannotUpdateWithNoInternet);
            }
        }

        /// <summary>
        /// Clicks "add book" when enter is pressed in title box of add book page.
        /// </summary>
        private void TitleAddBookTextBoxKeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                AddBookButtonClick(sender, e);
            }
        }

        /// <summary>
        /// Click handler for application bar reset progress button click.
        /// </summary>
        private void ResetProgressClick(object sender, EventArgs e)
        {
            if (!_resetting)
            {
                if (App.CurrentUser.DownloadedUserBooks.Any())
                {
                    _resetting = true;
                    GlobalViewModel.ShowToast(AppResources.Notification, AppResources.SelectBooksToReset);
                    booksListBox.SelectionMode = SelectionMode.Multiple;
                    BookViewModel.EnableAppBarIcons(false);
                    BookViewModel.ShowCheckBoxes(booksListBox, true, true);
                }
                else
                {
                    GlobalViewModel.ShowToast(AppResources.Notification, AppResources.CannotResetWithNoBooks);
                }
            }
            else
            {
                GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.ResettingInProgress);
                var books = booksListBox.SelectedItems;
                ResetBooks(books);
                PostBooksUpdate();
            }
        }

        /// <summary>
        /// Click handler for application bar add button click.
        /// </summary>
        private void AddButtonClick(object sender, EventArgs e)
        {
            BookViewModel.CheckAndSelectPivotItem(booksPivot, addBooksPivotItem, myBooksPivotItem);
        }

        /// <summary>
        /// Click handler for add book button click. Validates fields and initiates book add request.
        /// </summary>
        private void AddBookButtonClick(object sender, RoutedEventArgs e)
        {
            if (!AreAddFieldsValid()) return;
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                var email = emailAddBookTextBox.Text.ToLower();
                var title = titleAddBookTextBox.Text.ToLower();

                GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.AddingBookProgress);
                GlobalViewModel.ShowCancelButton(true, cancelGridAddBooks);
                _bookViewModel.AddBookWebRequest(email, title, App.CurrentUser, AddCallback);
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Error, AppResources.InternetProblem);
            }
        }

        /// <summary>
        /// Click handler for application bar menu item "log out". Logs the user out and sends to login page.
        /// </summary>
        private void LogOutClick(object sender, EventArgs e)
        {
            NotificationTool.Show(AppResources.Confirmation, AppResources.ConfirmLogout,
                    new NotificationAction(AppResources.Yes, "OKActionTemplateKey", LogOut),
                    new NotificationAction(AppResources.No, "CancelActionTemplateKey", () => { }));
        }

        /// <summary>
        /// Click handler tap and hold contextmenu for book delete
        /// </summary>
        private void DeleteBookMenuItemClick(object sender, RoutedEventArgs e)
        {
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                var frameworkElement = sender as FrameworkElement;
                if (frameworkElement != null)
                {
                    var book = frameworkElement.DataContext as Book;
                    var books = new List<Book> { book };
                    NotificationTool.Show(AppResources.Confirmation, AppResources.ConfirmPermanentBookDelete,
                                          new NotificationAction(AppResources.Yes, "OKActionTemplateKey",
                                                                 () => DeleteBooks(books)),
                                          new NotificationAction(AppResources.No, "CancelActionTemplateKey", () => { }));
                }
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Notification, AppResources.CannotDeleteWithNoInternet);
            }
        }

        /// <summary>
        /// Click handler tap and hold contextmenu for book update
        /// </summary>
        private void UpdateBookMenuItemClick(object sender, RoutedEventArgs e)
        {
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                var frameworkElement = sender as FrameworkElement;
                if (frameworkElement != null)
                {
                    var book = frameworkElement.DataContext as Book;
                    var books = new List<Book> { book };
                    if (book == null) return;
                    if (!book.IsDownloaded)
                    {
                        GlobalViewModel.ShowToast(AppResources.Notification, AppResources.CannotUpdateNotDownloaded);
                    } else
                    {
                        BookViewModel.EnableAppBarIcons(false);
                        SyncBooks(books);
                    }
                }
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Notification, AppResources.CannotDeleteWithNoInternet);
            }
        }


        /// <summary>
        /// Click handler for application bar button "sync". Redownloads the books in user library
        /// </summary>
        private void SyncButtonClick(object sender, EventArgs e)
        {
            // If network is not available dont sync books.
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                if (!_updating)
                {
                    if (App.CurrentUser.DownloadedUserBooks.Any())
                    {
                        BookViewModel.ShowCheckBoxes(booksListBox, true, true);
                        booksListBox.SelectionMode = SelectionMode.Multiple;
                        _updating = true;
                        GlobalViewModel.ShowToast("", AppResources.SelectBooksToUpdate);
                        BookViewModel.EnableAppBarIcons(false);

                    }
                    else
                    {
                        GlobalViewModel.ShowToast("", AppResources.CannotSyncWithNoBooks);
                    }
                }
                else
                {
                    var books = booksListBox.SelectedItems;
                    SyncButton.IsEnabled = false;
                    SyncBooks(books);
                }
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Error, AppResources.CannotSyncWithNoInternet);
            }
        }

        /// <summary>
        /// When a book is clicked on the my books page
        /// </summary>
        private void BooksListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // If multiple books are supposed to be selected for deletion do nothing
            if (_deleting)
            {
                CheckIfSelectOrDeselect(DeleteButton, e.RemovedItems, true);
                return;
            }
            // If multiple books are supposed to be selected for updating do nothing
            if (_updating)
            {
                CheckIfSelectOrDeselect(SyncButton, e.RemovedItems, false);
                return;
            }
            if (_resetting)
            {
                CheckIfSelectOrDeselect(ResetButton, e.RemovedItems, false);
                return;
            }
            var book = booksListBox.SelectedItem as Book;
            if (book == null) return;

            // If book is already downloaded, send to chapters page.
            if (App.CurrentUser.DownloadedUserBooks.Contains(book))
            {
                PopulateAndSendToChapters(book);
            }
            else
            {
                // If book is not downloaded, check network connection and then download book.
                if (DeviceNetworkInformation.IsNetworkAvailable)
                {
                    DownloadBooks(book);
                }
                else
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.CannotDownloadWithNoInternet);
                    booksListBox.SelectedIndex = -1;
                }
            }
        }

        /// <summary>
        /// When logo is clicked navigate to sponsor page.
        /// </summary>
        private void SponsorItemTap(object sender, GestureEventArgs e)
        {
            var stackpanel = sender as StackPanel;
            var grid = stackpanel.Parent as Grid;
            var pivot = grid.Parent as PivotItem;
            // Check internet connection BEFORE redirecting to sponsor.xaml
            if (DeviceNetworkInformation.IsNetworkAvailable)
            {
                NavigationService.Navigate(new Uri(String.Format("/View/Sponsor.xaml?{0}", pivot.Name), UriKind.Relative));
            }
            else
            {
                GlobalViewModel.ShowToast(AppResources.Error, AppResources.InternetProblem);
            }
        }

        /// <summary>
        /// When a chapter is clicked in the chapters list box
        /// </summary>
        private void ChaptersListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var chapter = chaptersListBox.SelectedItem as Chapter;
            if (chapter == null) return;
            if (chapter.IsDownloaded)
            {
                // Check if the chapter has any questions that have no been answered
                var firstQuestion = chapter.Questions.FirstOrDefault(x => x.AnsweredDate < DateTime.Now);
                if (firstQuestion != null)
                {
                    App.CurrentUser.CurrentChapter = chapter;
                    // If current question is not set, just take the first question.
                    if (App.CurrentUser.CurrentChapter.CurrentQuestion == null || (App.CurrentUser.CurrentChapter.CurrentQuestion.AnsweredDate >= DateTime.Now))
                    {
                        App.CurrentUser.CurrentChapter.CurrentQuestion = firstQuestion;
                    }
                    NavigationService.Navigate(new Uri("/View/Study.xaml", UriKind.Relative));
                }
                else
                {
                    // Check if chapter has any questions at all.
                    if (chapter.Questions.Any())
                    {
                        App.CurrentUser.CurrentChapter = chapter;
                        NotificationTool.Show(AppResources.Congratulation, AppResources.NoQuestionsRemaining,
                            new NotificationAction(AppResources.Reset, () =>
                            {
                                StudyViewModel.ResetProgress(App.CurrentUser);
                                NavigationService.Navigate(new Uri("/View/Study.xaml", UriKind.Relative));
                            }),
                            new NotificationAction(AppResources.Cancel, "CancelActionTemplateKey", () => { }));
                    }
                    else
                    {
                        GlobalViewModel.ShowToast(AppResources.Error, AppResources.NoQuestions);
                    }
                    chaptersListBox.SelectedIndex = -1;
                }
            }
            else
            {
                if (DeviceNetworkInformation.IsNetworkAvailable)
                {
                    GlobalViewModel.ShowProgressBar(trayProgressBar, AppResources.DownloadInProgress);
                    GlobalViewModel.ShowCancelButton(true, cancelGridChapters);
                    BookViewModel.EnableListBox(false, chaptersListBox);
                    _bookViewModel.GetQuestionsWebRequest(chapter, null, GetQuestionsManualCallback);
                }
                else
                {
                    GlobalViewModel.ShowToast(AppResources.Error, AppResources.CannotAddChapterWithNoInternet);
                }
            }
        }
        #endregion
    }
}